home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
doom
/
ldhe-src.0
/
ldhe-src
/
dehacked
/
source
/
dehacked.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-02
|
20KB
|
827 lines
// DeHackEd version 2.3
// Written by Greg Lewis, gregl@umich.edu
// If you release any versions of this code, please include
// the author in the credits. Give credit where credit is due!
#include <sys/param.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "version.h"
#include "dehacked.h"
#include "dheinit.h"
#undef TEXT
#undef WIDTH
#undef HEIGHT
#include "ttyobj.h"
#ifdef linux
void quit(int sig)
{
fprintf(stderr, "Exiting with signal %d\n", sig);
exit(0);
}
#else
#ifdef _SGI_SOURCE
void quit(int sig, ...)
{
fprintf(stderr, "Exiting with signal %d\n", sig);
exit(0);
}
#else
#error What machine are we compiling on?
#endif /* _SGI_SOURCE */
#endif /* linux */
int main (int argc, char *argv[])
{
EBool ExitLoop = NO; // Are we out of the main loop?
/* Set up the signals to reset the console at exit */
signal(SIGHUP, quit);
signal(SIGINT, quit);
signal(SIGQUIT, quit);
signal(SIGBUS, quit);
signal(SIGSEGV, quit);
signal(SIGTERM, quit);
// Lose suid capabilities...
setuid(getuid());
// Initialize memory according to largest values needed
thingdata = new unsigned long[numobj[THING][DOOM2_0]][THING_FIELDS];
sounddata = new unsigned long[numobj[SOUND][DOOM2_0]][SOUND_FIELDS];
framedata = new unsigned long[numobj[FRAME][DOOM2_0]][FRAME_FIELDS];
spritedata = new unsigned long[numobj[SPRITE][DOOM2_0]];
maxammodata= new unsigned long[numobj[AMMO][DOOM2_0]];
perammodata= new unsigned long[numobj[AMMO][DOOM2_0]];
weapondata = new unsigned long[numobj[WEAPON][DOOM2_0]][WEAPON_FIELDS];
#ifdef TEXT_DATASIZE
textdatap = new char[TEXT_DATASIZE];
#else
#error "DOOM1_9" should be replaced with the version with the largest text segment.
textdatap = new char[size[TXT][DOOM1_9]];
#endif
// Make sure we've got the necessary memory
if (thingdata == NULL || sounddata == NULL || maxammodata == NULL ||
spritedata == NULL || framedata == NULL || perammodata == NULL ||
weapondata == NULL || textdatap == NULL)
AbortProg("in Main");
// Initialize the thingdata to all 0's, mostly for the Clipboard.
memset(thingdata, 0, size[THING][DOOM2_0]*numobj[THING][DOOM2_0]);
getcwd(curdir, MAXPATHLEN);
// Load config file
Parseconfigfile();
// Check out command line arguments
if (Parsecommandline(argc, argv) == -1)
ExitLoop = YES;
else
{
// Initialize Linux specific subsystems.
if ( linux_init() < 0 )
exit(-1);
atexit(linux_end);
// Since it's not command-line only, change to 50 line mode,
// draw the Thing windows, and the intro screen.
batch = NO;
textmode(C4350);
_setcursortype(_NOCURSOR);
WipeScreen();
#ifdef HAVE_MOUSE
InitMouse();
#endif
Printfunc[THING]();
redraw = NOT;
Printintro();
}
while (ExitLoop == NO)
{
// Redraw the correct screen
if (redraw != NOT) {
if (redraw == ALL)
WipeScreen();
// Draw the correct screen according to current mode
Printfunc[mode]();
redraw = NOT;
}
// Highlight the current field
Highlight(NHILIT);
// The Process functions return a YES if the user is exiting...
if (Waitforevent(NO))
ExitLoop = ProcessKeypress();
#ifdef HAVE_MOUSE
else
ExitLoop = ProcessMouse();
#endif
// Close the mouse, clear the screen, print exiting message.
if (ExitLoop == YES)
{
#ifdef HAVE_MOUSE
CloseMouse();
#endif
textmode(C80);
cls();
gotoxy(1,1);
textattr(0);
tty_raw(NO);
puts("Exiting...");
puts("Bye!");
}
}
// Delete allocated memory
delete[] thingdata;
delete[] sounddata;
delete[] framedata;
delete[] spritedata;
delete[] maxammodata;
delete[] perammodata;
delete[] weapondata;
delete[] textdatap;
// Close open files
fclose(doomexefp);
fclose(doomwadfp);
fclose(doombakfp);
return 0;
}
// Aborts program when out of memory, and asks if the user wants to
// write the changes first. I hate it when DEU just crashes!
void AbortProg(char *func)
{
int result;
// Crash to text mode, let the user know what happened, and how much
// memory is still left.
clearscreen(0, ' ');
textmode(C80);
gotoxy(1,1);
textattr(0);
printf("Out of memory %s!\n", func);
// If there are changes to write, ask the user if he/she wants to write
// them. This *should* work even if we have 0 memory free.
if (changes == YES)
{
puts("Do you want to write all changes to the exe file? ");
result = getch();
if (tolower(result) == 'y')
{
Writedoom();
puts("Changes written.\n");
}
else
puts("Changes not written.\n");
}
puts("Have a nice day. Try again later with a bit more memory");
puts("free...");
exit(1);
}
// Changes to a new editing mode
void Changemode(EModes newmode)
{
// Redraw only the data if the person tries to switch the mode he's
// currently in.
if (newmode != mode)
redraw = ALL;
else
redraw = ALLDATA;
mode = newmode;
// Verify that the current item is onscreen for the different
// lists... ie, if we're on Frame #562, make sure that one is somewhere
// on the screen. Adjust TOPROW accordingly.
if (global[mode][CUR] < global[mode][TOPROW] ||
global[mode][CUR] > global[mode][TOPROW] + 37)
{
if (global[mode][CUR] < 18)
global[mode][TOPROW] = 0;
else if (global[mode][CUR] > global[mode][GMAX]-global[mode][GMIN]-37)
global[mode][TOPROW] = global[mode][GMAX]-global[mode][GMIN]-37;
else
global[mode][TOPROW] = global[mode][CUR] - 18;
}
else if (redraw == ALLDATA)
redraw = NOT;
}
// Copies from one object to another.
int Getcopyinfo(void)
{
int minnum = global[mode][GMIN];
int fromnum, tonum;
char buffer[40];
char temp[40];
// Text strings can't be copied due to the stringent length restrictions.
// It's possible to, but very very few of the strings are = in length.
if (mode == TEXT_EDIT)
{
Printwindow("Text strings cannot be copied.", ERROR);
return -1;
}
sprintf(buffer, "Enter the %s number to copy from:", objnames[mode]);
if (Printinputwindow(temp, buffer, LONGINT, 12) == -1)
return -1;
// Verify the from number to make sure it's not invalid.
fromnum = atoi(temp);
if (fromnum < minnum || fromnum > global[mode][GMAX])
{
sprintf(buffer, "Invalid %s number!", objnames[mode]);
Printwindow(buffer, ERROR);
return -1;
}
sprintf(buffer, "Enter the %s number to copy to:", objnames[mode]);
if (Printinputwindow(temp, buffer, LONGINT, 12) == -1)
return -1;
// Verify the to number to make sure it's not invalid.
tonum = atoi(temp);
if (tonum < minnum || tonum > global[mode][GMAX])
{
sprintf(buffer, "Invalid %s number!", objnames[mode]);
Printwindow(buffer, ERROR);
return -1;
}
// OK, the dope is trying to copy something over itself.
if (fromnum == tonum)
{
sprintf(buffer, "Duplicate %s number!", objnames[mode]);
Printwindow(buffer, ERROR);
return -1;
}
// Compensate for different starting indices in the objects, ie, Things
// start at #1, whereas Frames start at #0.
tonum -= minnum;
fromnum -= minnum;
// And do the copy...
switch (mode)
{
case THING_EDIT:
case THING_LIST:
memcpy(&thingdata[tonum], &thingdata[fromnum], size[THING][version]);
break;
case FRAME_EDIT:
memcpy(&framedata[tonum], &framedata[fromnum], size[FRAME][version]);
break;
case AMMO_EDIT:
memcpy(&weapondata[tonum], &weapondata[fromnum], size[WEAPON][version]);
break;
case SPRITE_EDIT:
memcpy(&spritedata[tonum], &spritedata[fromnum], size[SPRITE][version]);
break;
case SOUND_EDIT:
memcpy(&sounddata[tonum], &sounddata[fromnum], size[SOUND][version]);
break;
}
return 0;
}
// Go to a given object for any mode
int GotoObject(int firstdigit)
{
char prompt[30];
char buffer[20];
long num;
int min = global[mode][GMIN];
EBool error = NO;
sprintf(prompt, "Enter a new %s number:", objnames[mode]);
if (Printinputwindow(buffer, prompt, LONGINT, 0, firstdigit) == -1)
return -1;
// Check for negative numbers, at least.
num = atol(buffer);
if (num < 0)
{
Printwindow("Negative numbers are invalid.", ERROR);
return -1;
}
// Make sure it's a valid object to go to. Note that for the THING_EDIT
// window we need to do a special return of the correct number to go to.
// All other modes, just go there.
switch (mode)
{
case THING_EDIT:
if (num > global[THING][GMAX])
error = YES;
else
return num - min;
break;
case FRAME_EDIT:
case AMMO_EDIT:
case SOUND_EDIT:
case SPRITE_EDIT:
case THING_LIST:
if (num > global[mode][GMAX])
error = YES;
else
global[mode][CUR] = (int)(num - min);
break;
case TEXT_EDIT:
if (num > size[TXT][version])
error = YES;
else
global[mode][CUR] = Gettextnum(num+toff[version]);
break;
}
// Invalid number to go to
if (error == YES)
{
Printwindow("That value is too high!", ERROR);
return -1;
}
// Make it so.
Changemode(mode);
return 0;
}
// Highlights the current field (equivalent to unhighlighting if the
// NORMAL attribute is given).
void Highlight(unsigned char attribute)
{
int ref = posinfo[mode][0] + global[mode][FIELD] - 1;
int i, row;
if (mode == THING_EDIT || mode == AMMO_EDIT)
row = Fielddata[ref][0];
else
row = global[mode][CUR] - global[mode][TOPROW] + 7;
// Check the super duper master array of highlight locations, and
// change the color of whatever information is there accordingly.
for (i=Fielddata[ref][3]-1; i < Fielddata[ref][4]; i++)
highlight_loc(i+1, row+1, attribute);
}
// Handles the command line arguments. Also opens the doom.exe file.
// Current verification of doom.exe is the file size.
int Parsecommandline(int argc, char *argv[])
{
int i = 1;
EBool quit = NO;
char buffer[160] = "";
Printtextintro();
// If there was a command-line path for doom specified, make sure
// the path ends in a backslash.
if (argc > 1 && argv[1][0] != '-')
{
strcpy(buffer, argv[i++]);
if (buffer[strlen(buffer)-1] != '\\' && strlen(buffer) != 0)
strcat(buffer, "\\");
}
if (GetDoomFiles(buffer) == -1)
return -1;
// Verify size
fseek(doomexefp, 0, SEEK_END);
if (ftell(doomexefp) == SIZE1_12)
{
version = DOOM1_2;
truever = DOOM1_12;
puts("Using registered Doom v1.2.");
}
else if (ftell(doomexefp) == SIZE1_16)
{
version = DOOM1_6;
truever = DOOM1_16;
puts("Using registered Doom v1.666.");
}
else if (ftell(doomexefp) == SIZE2_16)
{
version = DOOM2_0;
truever = DOOM2_16;
puts("Using Doom 2, Hell On Earth, v1.666.");
}
else if (ftell(doomexefp) == SIZE2_17)
{
version = DOOM1_6;
truever = DOOM2_17;
puts("Using Doom 2, Hell On Earth, v1.7.");
}
else if (ftell(doomexefp) == SIZE2_17A)
{
version = DOOM1_6;
truever = DOOM2_17A;
puts("Using Doom 2, Hell On Earth, v1.7a.");
}
else if (ftell(doomexefp) == SIZE2_19)
{
version = DOOM1_9;
truever = DOOM2_19;
puts("Using registered Doom v1.9.");
}
else if (ftell(doomexefp) == SIZE2_18)
{
puts("\nDoom v1.8 exe found... DeHackEd v2.3 doesn't support Doom 1.8.");
puts("Upgrade to version 1.9, and you'll be all set!");
return -1;
}
else if (ftell(doomexefp) == SIZEX_18) {
version = LNX_X18;
truever = DOOMX_18;
Lnx_DOOM = YES;
puts("Using registered Linux X11 Doom v1.9.");
}
else if (ftell(doomexefp) == SIZES_18) {
version = LNX_S18;
truever = DOOMS_18;
Lnx_DOOM = YES;
puts("Using registered Linux SVGA Doom v1.9.");
}
else if (ftell(doomexefp) == SIZE_SGI16) {
puts("SGI X11 Doom version 1.666 not supported!\n");
return -1;
}
else if (ftell(doomexefp) == SIZE_SGI18) {
version = SGI_X18;
truever = DOOM_SGI;
puts("Using registered SGI X11 Doom v1.9.");
}
else if (ftell(doomexefp) == doomsize)
printf("Using user-specified Doom size, %ld.", doomsize);
else
{
puts("Unknown Doom exe file size!");
puts("You may have a modified Doom exe file, or you are using an");
puts("unknown version of Doom. Do you wish to continue?");
puts("If you are not positive about this, answer no!");
char key = getch();
if (tolower(key) != 'y')
return -1;
}
// OK, load the stuff
Loaddoom(doomexefp);
// Set the highest object number for each object. This is necessary
// for Doom because we are dealing with multiple version numbers and
// have to watch out in case the user specifies a strange version number
// manually.
global[0][GMAX] = numobj[THING][version];
global[1][GMAX] = numobj[FRAME][version] - 1;
global[2][GMAX] = numobj[WEAPON][version];
global[3][GMAX] = numobj[SOUND][version];
global[4][GMAX] = numobj[SPRITE][version] - 1;
global[5][GMAX] = numobj[TXT][version] - 1;
global[6][GMAX] = numobj[THING][version] - 1;
// Parse all the command line args
for (; i<argc; i++)
{
if (stricmp(argv[i], "-load") == 0)
{
strcpy(buffer, argv[++i]);
printf("Loading patch file: %s\n\t", buffer);
if (Loadpatch(buffer) != ERROR)
Writedoom();
quit = YES;
}
else if (stricmp(argv[i], "-save") == 0)
{
int x, y, result;
strcpy(buffer, argv[++i]);
printf("Saving patch file: %s\n\t", buffer);
wherexy(&x, &y);
result = Savepatch(buffer, NO);
if (result == -1)
{
cputs("File exists! Overwrite? ");
result = getch();
gotoxy(x, y);
clreol();
if (tolower(result) != 'y')
strcpy(buffer, "Write canceled.");
else
Savepatch(buffer, YES);
}
puts(buffer);
quit = YES;
}
else if (stricmp(argv[i], "-reload") == 0)
{
Loaddoom(doombakfp);
Writedoom();
printf("Doom data reloaded from %s.\n", doombak);
quit = YES;
}
else
{
printf(" Cannot parse command \"%s\"!\n", argv[i]);
Printoptions();
return -1;
}
}
// quit will be set if we are working on some command line arguments
// and don't actually want to edit interactively.
if (quit)
return -1;
else
return 0;
}
// Run Doom. This sucker's tricky. Probably the wrong way to do it too,
// but I'm not sure of a better way.
int RunExe(void)
{
char buffer[80];
char *argv[20];
int i=2, j;
// Check if the doompath actually exists.
if (chdir(doompath) == -1)
{
sprintf(buffer, "Could not switch to %s!", doompath);
Printwindow(buffer, ERROR);
return -1;
}
// Init 'em to NULL, if that helps at all. Hopefully prevents garbage
// arguments from getting passed to Doom.
for (j=0; j<20; j++)
argv[j] = NULL;
strcpy(buffer, doomargs);
argv[0] = doomexe;
argv[1] = buffer;
// Parse the doomargs into separate arguments.
// Not sure if this is necessary, but it seems to work.
for (j=0; j<strlen(doomargs); j++)
{
if (buffer[j] == ' ' || buffer[j] == '\t')
{
buffer[j] = 0;
if (argv[i-1] == &(buffer[j]))
argv[i-1] = &(buffer[j+1]);
else
{
argv[i] = &(buffer[j+1]);
i++;
}
}
else if (buffer[j] == '\r' || buffer[j] == '\n')
break;
}
buffer[j] = 0;
// Change the screen mode, close files, etc.
textmode(C80);
#ifdef HAVE_MOUSE
CloseMouse();
#endif
fclose(doomexefp);
fclose(doomwadfp);
fclose(doombakfp);
tty_raw(NO);
// Now try to actually run it.
spawn(doomexe, argv);
// Set things up again. re-open Doom files, init mouse, etc.
tty_raw(YES);
if (GetDoomFiles("") == -1)
exit (1);
#ifdef HAVE_MOUSE
InitMouse();
#endif
textmode(C4350);
_setcursortype(_NOCURSOR);
chdir(curdir);
redraw = ALL;
return 0;
}
// Updates an ammo field with new information.
int Updateammo(void)
{
char order[5] = {BOB1FRAME, BOB2FRAME, BOB3FRAME, SHOOTFRAME, FIREFRAME};
int curfield = global[AMMO_EDIT][FIELD];
int curnum = global[AMMO_EDIT][CUR];
char buffer[20];
char prompt[20];
long num;
// If this is a field that shouldn't be edited, return. Ammo needs a
// special check, because the ammo can't be edited if it's "N/A", not
// a know ammo value.
if (curfield < 1 || curfield > 8 ||
((weapondata[curnum][AMMOTYPE] >= numobj[AMMO][version]) &&
(curfield == 2 || curfield == 3)))
return -1;
// Get the new value
sprintf(prompt, "%s:", fullwepfields[curfield-1]);
if (Printinputwindow(buffer, prompt, LONGINT, 0) == -1)
return -1;
num = atol(buffer);
// Update the correct ammo or weapon data array with the new info.
if (curfield == 1)
weapondata[curnum][AMMOTYPE] = num;
else if (curfield == 2)
maxammodata[weapondata[curnum][AMMOTYPE]] = num;
else if (curfield == 3)
perammodata[weapondata[curnum][AMMOTYPE]] = num;
else
weapondata[curnum][order[curfield-4]] = num;
return 0;
}
// Updates a Frame record and field with new info.
int Updateframe(void)
{
char order[6] = {SPRITENUM, SPRITESUB, 0, NEXTFRAME, DURATION, ACTIONPTR};
int curfield = global[FRAME_EDIT][FIELD];
int curnum = global[FRAME_EDIT][CUR];
char buffer[20];
char prompt[20];
// Can't edit an invalid field
if (curfield < 1 || curfield > 6)
return -1;
curfield--;
// Don't print the input box if we're on the "Bright Sprite" field.
if (curfield != 2)
{
sprintf(prompt, "%s:", framefields[order[curfield]]);
if (Printinputwindow(buffer, prompt, LONGINT, 0) == -1)
return -1;
}
if ((curfield == SPRITESUB) && (framedata[curnum][SPRITESUB] & (1L << 15)))
framedata[curnum][SPRITESUB] = (atol(buffer)) ^ (1L << 15);
else if (curfield == 2)
framedata[curnum][SPRITESUB] ^= (1L << 15);
else
framedata[curnum][order[curfield]] = atol(buffer);
return 0;
}
// Updates a Sound record and field with new info
int Updatesound(void)
{
int curfield = global[SOUND_EDIT][FIELD];
int curnum = global[SOUND_EDIT][CUR];
char order[3] = {TEXTP, ZERO_ONE, VALUE};
char buffer[20];
char prompt[20];
if (curfield < 1 || curfield > 3)
return -1;
sprintf(prompt, "%s:", soundfields[order[curfield-1]]);
if (Printinputwindow(buffer, prompt, LONGINT, 0) == -1)
return -1;
curfield--;
if (curfield == 0)
sounddata[curnum][TEXTP] = atol(buffer) + toff[version];
else
sounddata[curnum][order[curfield]] = atol(buffer);
return 0;
}
// Updates the Sprite array with new info
int Updatesprite(void)
{
char buffer[20];
if (Printinputwindow(buffer, "Enter a new value:", LONGINT, 0) == -1)
return -1;
spritedata[global[mode][CUR]] = atol(buffer) + toff[version];
return 0;
}
// Wrapper function to update the text section
int Updatetext(void)
{
return Inputtext(NO);
}
// Updates a Thing record with new info to a certain field from the Thinglist.
int Updatethingl(void)
{
char order[4] = {IDNUM, HP, SPEED, MISSILEDAMAGE};
int curfield = global[THING_LIST][FIELD];
int curnum = global[THING_LIST][CUR];
char buffer[20];
char prompt[20];
if (curfield < 1 || curfield > 4)
return -1;
curfield--;
sprintf(prompt, "%s:", thingfields[order[curfield]]);
if (Printinputwindow(buffer, prompt, LONGINT, 0) == -1)
return -1;
if (curfield == 2 && ((curnum != 0) && (thingdata[curnum][BITS] & 65536L)))
thingdata[curnum][order[curfield]] = (atol(buffer)) << 16;
else
thingdata[curnum][order[curfield]] = atol(buffer);
return 0;
}
// Updates a Thing record with new info to a certain field.
int Updatethings(void)
{
int curfield = global[THING_EDIT][FIELD];
int curnum = global[THING_EDIT][CUR];
char buffer[20];
char prompt[20];
// Return on an invalid field
if (curfield < 1 || curfield > 55 ||
(curfield == 24 && version == DOOM1_2))
return -1;
curfield--;
// Check if this is a non-bit field that we're editing
if (curfield < 23)
{
// If so, print the prompt and get a new value.
sprintf(prompt, "%s:", thingfields[thingorder[curfield]]);
if (Printinputwindow(buffer, prompt, LONGINT, 0) == -1)
return -1;
// Replace the Thing values, being very careful of the fields that
// are multiplied by 65536.
if (thingorder[curfield] == CWIDTH || thingorder[curfield] == CHEIGHT ||
((thingorder[curfield] == SPEED) &&
((curnum != 0) && (thingdata[curnum][BITS] & 65536L))))
thingdata[curnum][thingorder[curfield]] = (atol(buffer)) << 16;
else
thingdata[curnum][thingorder[curfield]] = atol(buffer);
}
else
// Otherwise just update the correct bit in the correct Bits field
// by XORing it with itself.
thingdata[curnum][BITS] ^= (1L << (curfield-23));
return 0;
}